package scuff.js

import org.mozilla.javascript._
import java.io.{ Reader, InputStreamReader, BufferedReader }

object LESSCompiler {

  def apply(options: (Symbol, Any)*): LESSCompiler = {
    val reader = new InputStreamReader(getClass().getResourceAsStream("/META-INF/script/less-1.2.2.js"), "UTF-8")
    this.apply(reader, options: _*)
  }
  def apply(lessCompilerSource: Reader, options: (Symbol, Any)*): LESSCompiler = {
    val optionString = toJavascript(options)
    new LESSCompiler(lessCompilerSource, optionString)
  }

}

class LESSCompiler private (compilerSource: Reader, options: String) {
  private final val lessCodeVarName = "LESSCode"

  private val jsCompile = "less.compile(%s);".format(lessCodeVarName)

  private val globalScope = try {
    withContext { context ⇒
      val globalScope = context.initStandardObjects()
      context.evaluateString(globalScope, "location = {port:''};", "fake location", 1, null)
      context.evaluateString(globalScope, "window = {location:location};", "fake window", 1, null)
      context.evaluateString(globalScope, "window.less = {};", "fake window.less", 1, null)
      context.evaluateString(globalScope, "window.less.env = 'production';", "fake env", 1, null)
      context.evaluateString(globalScope, "less = window.less;", "fake env", 1, null)
      context.evaluateString(globalScope, "document = {getElementsByTagName: function(e){return [];}};", "fake document", 1, null)
      context.evaluateReader(globalScope, compilerSource, "less.js", 0, null)
      context.evaluateString(globalScope, """
         less.compile = function(inLESS) {
              var outCSS;
              new less.Parser(%1$s).parse(inLESS,function(err,tree) {
                  outCSS = err ? err : tree.toCSS(%1$s);
              });
              return outCSS;
         };
      """.format(options), "LESS compile function", 1, null)
      globalScope
    }
  } finally {
    compilerSource.close()
  }

  def compile(less: String, name: String = ""): String = withContext { context ⇒
    val compileScope = context.newObject(globalScope)
    compileScope.setParentScope(globalScope)
    compileScope.put(lessCodeVarName, compileScope, less)
    String.valueOf(context.evaluateString(compileScope, jsCompile, name, 0, null))
  }

}
